#pragma once
#include<queue>
#include<pthread.h>
using std::queue;

namespace wyl
{
    template<class T>
    class BlockQueue
    {
    public:
        BlockQueue(int cap = 5) : cap_(cap)
        {
            pthread_mutex_init(&mutex_,nullptr);
            pthread_cond_init(&is_empty,nullptr);
            pthread_cond_init(&is_full,nullptr);
        } 
        ~BlockQueue()
        {
            pthread_mutex_destroy(&mutex_);
            pthread_cond_destroy(&is_empty);
            pthread_cond_destroy(&is_full);
        }

        void push(T& t)
        {
            //生产者生产
            Lock();
            if(IsFull())
            {
                //如果生产者满了，那么生产进入等待。
                ProducerWait();
            }
            q.push(t);
            //唤醒消费者
            WakeupConsumer();
            Unlock();
        }
        void pop(T* out)
        {
            //消费者消费
            Lock();
            if(IsEmpty())
            {
                ConsumerWait();
            }
            *out = q.front();
            q.pop();
            WakeupProducer();
            Unlock();
        }

    private:
        size_t cap_;
        queue<T> q;
        pthread_mutex_t mutex_;
        pthread_cond_t is_empty;
        pthread_cond_t is_full;

        void Lock()
        {
            pthread_mutex_lock(&mutex_);
        }
        void Unlock()
        {
            pthread_mutex_unlock(&mutex_);
        }

        bool IsFull()
        {
            return cap_ == q.size();
        }
        bool IsEmpty()
        {
            return cap_ == 0;
        }

        void ProducerWait()
        {
            pthread_cond_wait(&is_full,&mutex_); 
        }
        void ConsumerWait()
        {
            pthread_cond_wait(&is_empty,&mutex_);
        }
        void WakeupConsumer()
        {
            pthread_cond_signal(&is_empty);
        }
        void WakeupProducer()
        {
            pthread_cond_signal(&is_full);
        }
    };
}